package com.mezz.bones.framework.base.tree;

import cn.hutool.core.util.StrUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class TreeBuilder<T, Info> {

    /**
     * 构建树结构
     *
     * @param items        数据列表
     * @param idGetter     获取节点ID的函数
     * @param parentIdGetter 获取父节点ID的函数
     * @param nameGetter   获取节点名称的函数
     * @param infoGetter   获取节点附加信息的函数
     * @return 树的根节点列表
     */
    public List<TreeNode<Info>> buildTree(List<T> items,
                                          Function<T, String> idGetter,
                                          Function<T, String> parentIdGetter,
                                          Function<T, String> nameGetter,
                                          Function<T, Info> infoGetter) {
        Map<String, TreeNode<Info>> nodeMap = new HashMap<>();
        List<TreeNode<Info>> rootNodes = new ArrayList<>();
        List<TreeNode<Info>> allNodes = new ArrayList<>();

        // 将每个数据项转换为 TreeNode，并存入 Map
        for (T item : items) {
            String id = idGetter.apply(item);
            String parentId = parentIdGetter.apply(item);
            String name = nameGetter.apply(item);
            Info info = infoGetter.apply(item);
            TreeNode<Info> node = new TreeNode<>(id, parentId, name, info);
            
            // 设置初始的 parentIds，根节点直接使用自己的ID
            if (StrUtil.isBlank(parentId) || !nodeMap.containsKey(parentId)) {
                node.setParentIds(id);  // 根节点的 parentIds 直接是自己的ID
                node.setLevel(1);
            }
            
            nodeMap.put(id, node);
            allNodes.add(node);
        }

        // 构建树结构
        for (TreeNode<Info> node : allNodes) {
            String parentId = node.getParentId();
            if (StrUtil.isBlank(parentId) || !nodeMap.containsKey(parentId)) {
                // 如果 parentId 为空或不存在于 Map 中，说明是根节点
                rootNodes.add(node);
            } else {
                // 否则找到父节点，并将当前节点添加到父节点的子节点列表中
                TreeNode<Info> parent = nodeMap.get(parentId);
                parent.addChild(node);
            }
        }

        return rootNodes;
    }

    public List<TreeNode<Info>> buildTree(List<T> items,
                                          Function<T, String> idGetter,
                                          Function<T, String> parentIdGetter,
                                          Function<T, String> nameGetter) {
        return buildTree(items, idGetter, parentIdGetter, nameGetter, item -> null);
    }

    /**
     * 树节点搜索数据展示规则
     * 1. 节点名称模糊匹配（不区分大小写）
     * 2. 已匹配的节点默认展示其所有子节点
     * 3. 已匹配的节点默认展示其上级路径节点
     * @param nodes 要搜索的树节点列表
     * @param searchName 搜索关键字
     */
    public void filterTree(List<TreeNode<Info>> nodes, String searchName) {
        if (CollectionUtils.isEmpty(nodes) || StringUtils.isEmpty(searchName)) {
            return;
        }

        // 转换搜索关键字为小写，避免重复转换
        String searchLower = searchName.toLowerCase();
        
        // 第一步：标记所有匹配的节点及其子节点
        Set<String> matchedIds = new HashSet<>();
        markMatchedNodesAndChildren(nodes, searchLower, matchedIds);
        
        // 第二步：标记所有匹配节点的父路径
        markParentPath(nodes, matchedIds);
        
        // 第三步：移除未匹配的节点
        filterUnmatchedNodes(nodes, matchedIds);
    }
    
    /**
     * 标记匹配的节点及其所有子节点
     */
    private void markMatchedNodesAndChildren(List<TreeNode<Info>> nodes, String searchLower, Set<String> matchedIds) {
        for (TreeNode<Info> node : nodes) {
            if (isNodeMatch(node, searchLower)) {
                // 如果节点匹配，标记该节点及其所有子节点
                markNodeAndChildren(node, matchedIds);
            }
            // 递归处理子节点
            if (!CollectionUtils.isEmpty(node.getChildren())) {
                markMatchedNodesAndChildren(node.getChildren(), searchLower, matchedIds);
            }
        }
    }
    
    /**
     * 检查节点是否匹配搜索条件
     */
    private boolean isNodeMatch(TreeNode<Info> node, String searchLower) {
        return node.getName() != null && 
               node.getName().toLowerCase().contains(searchLower);
    }
    
    /**
     * 标记节点及其所有子节点
     */
    private void markNodeAndChildren(TreeNode<Info> node, Set<String> matchedIds) {
        if (node == null || matchedIds.contains(node.getId())) {
            return;
        }
        matchedIds.add(node.getId());
        if (!CollectionUtils.isEmpty(node.getChildren())) {
            for (TreeNode<Info> child : node.getChildren()) {
                markNodeAndChildren(child, matchedIds);
            }
        }
    }
    
    /**
     * 标记所有匹配节点的父路径
     */
    private void markParentPath(List<TreeNode<Info>> nodes, Set<String> matchedIds) {
        Map<String, TreeNode<Info>> idToNodeMap = new HashMap<>();
        buildNodeMap(nodes, idToNodeMap);
        
        Set<String> parentIds = new HashSet<>(matchedIds);
        for (String id : matchedIds) {
            TreeNode<Info> node = idToNodeMap.get(id);
            while (node != null && !StringUtils.isEmpty(node.getParentId()) 
                    && !"0".equals(node.getParentId())) {  // 添加对"0"的判断
                parentIds.add(node.getParentId());
                node = idToNodeMap.get(node.getParentId());
            }
        }
        matchedIds.addAll(parentIds);
    }
    
    /**
     * 构建节点ID到节点的映射关系
     */
    private void buildNodeMap(List<TreeNode<Info>> nodes, Map<String, TreeNode<Info>> idToNodeMap) {
        for (TreeNode<Info> node : nodes) {
            idToNodeMap.put(node.getId(), node);
            if (!CollectionUtils.isEmpty(node.getChildren())) {
                buildNodeMap(node.getChildren(), idToNodeMap);
            }
        }
    }
    
    /**
     * 移除未匹配的节点
     */
    private void filterUnmatchedNodes(List<TreeNode<Info>> nodes, Set<String> matchedIds) {
        Iterator<TreeNode<Info>> iterator = nodes.iterator();
        while (iterator.hasNext()) {
            TreeNode<Info> node = iterator.next();
            if (!matchedIds.contains(node.getId())) {
                iterator.remove();
            } else if (!CollectionUtils.isEmpty(node.getChildren())) {
                filterUnmatchedNodes(node.getChildren(), matchedIds);
            }
        }
    }
}